Istražite Python kreacijske obrasce dizajna: Singleton, Factory, Abstract Factory, Builder i Prototype. Naučite njihove implementacije, prednosti i primjene u stvarnom svijetu.
Python obrasci dizajna: Duboko zaronite u kreacijske obrasce
Obrasci dizajna su rješenja koja se mogu ponovno upotrijebiti za uobičajene probleme u dizajnu softvera. Oni pružaju nacrt kako riješiti te probleme, promičući ponovnu upotrebu koda, održivost i fleksibilnost. Kreacijski obrasci dizajna, konkretno, bave se mehanizmima stvaranja objekata, pokušavajući stvoriti objekte na način prikladan situaciji. Ovaj članak pruža sveobuhvatno istraživanje kreacijskih obrazaca dizajna u Pythonu, uključujući detaljna objašnjenja, primjere koda i praktične primjene relevantne za globalnu publiku.
Što su kreacijski obrasci dizajna?
Kreacijski obrasci dizajna apstrahiraju proces instanciranja. Oni odvajaju kôd klijenta od specifičnih klasa koje se instanciraju, omogućujući veću fleksibilnost i kontrolu nad stvaranjem objekata. Korištenjem ovih obrazaca možete stvoriti objekte bez specificiranja točne klase objekta koji će biti stvoren. Ovo odvajanje briga čini kôd robusnijim i lakšim za održavanje.
Primarni cilj kreacijskih obrazaca je apstrahirati proces instanciranja objekata, skrivajući složenost stvaranja objekata od klijenta. To omogućuje programerima da se usredotoče na logiku visoke razine svojih aplikacija, a da ih ne opterećuju sitni detalji stvaranja objekata.
Vrste kreacijskih obrazaca dizajna
U ovom ćemo članku obraditi sljedeće kreacijske obrasce dizajna:
- Singleton: Osigurava da klasa ima samo jednu instancu i pruža globalnu pristupnu točku do nje.
- Factory Method: Definira sučelje za stvaranje objekta, ali dopušta podklasama da odluče koju klasu instancirati.
- Abstract Factory: Pruža sučelje za stvaranje obitelji povezanih ili ovisnih objekata bez specificiranja njihovih konkretnih klasa.
- Builder: Odvaja konstrukciju složenog objekta od njegove reprezentacije, omogućujući istom procesu konstrukcije da stvori različite reprezentacije.
- Prototype: Specificira vrstu objekata koje treba stvoriti pomoću prototipne instance i stvara nove objekte kopiranjem ovog prototipa.
1. Singleton obrazac
Singleton obrazac osigurava da klasa ima samo jednu instancu i pruža globalnu pristupnu točku do nje. Ovaj je obrazac koristan kada je potreban točno jedan objekt za koordinaciju radnji u cijelom sustavu. Često se koristi za upravljanje resursima, zapisivanje ili postavke konfiguracije.
Implementacija
Evo Python implementacije Singleton obrasca:
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
# Primjer upotrebe
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # Izlaz: True
Objašnjenje:
_instance: Ova varijabla klase pohranjuje jednu instancu klase.__new__: Ova se metoda poziva prije__init__kada se stvori objekt. Provjerava postoji li već instanca. Ako ne postoji, stvara novu instancu pomoćusuper().__new__(cls)i pohranjuje je u_instance. Ako instanca već postoji, vraća postojeću instancu.
Slučajevi upotrebe
- Veza s bazom podataka: Osiguravanje da je samo jedna veza s bazom podataka otvorena u jednom trenutku.
- Upravitelj konfiguracije: Pružanje jedne pristupne točke postavkama konfiguracije aplikacije.
- Logger: Stvaranje jedne instance zapisivanja za obradu svih operacija zapisivanja u aplikaciji.
Primjer
Razmotrimo jednostavan primjer upravitelja konfiguracije implementiranog pomoću Singleton obrasca:
class ConfigurationManager(Singleton):
def __init__(self):
if not hasattr(self, 'config'): # Osigurajte da se __init__ poziva samo jednom
self.config = {}
def set_config(self, key, value):
self.config[key] = value
def get_config(self, key):
return self.config.get(key)
# Primjer upotrebe
config_manager1 = ConfigurationManager()
config_manager1.set_config('database_url', 'localhost:5432')
config_manager2 = ConfigurationManager()
print(config_manager2.get_config('database_url')) # Izlaz: localhost:5432
2. Factory Method obrazac
Factory Method obrazac definira sučelje za stvaranje objekta, ali dopušta podklasama da odluče koju klasu instancirati. Factory Method dopušta klasi da odgodi instanciranje podklasama. Ovaj obrazac promiče labavo povezivanje i omogućuje vam dodavanje novih vrsta proizvoda bez izmjene postojećeg koda.
Implementacija
Evo Python implementacije Factory Method obrasca:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
class AnimalFactory(ABC):
@abstractmethod
def create_animal(self):
pass
class DogFactory(AnimalFactory):
def create_animal(self):
return Dog()
class CatFactory(AnimalFactory):
def create_animal(self):
return Cat()
# Kôd klijenta
def get_animal(factory: AnimalFactory):
animal = factory.create_animal()
return animal.speak()
dog_sound = get_animal(DogFactory())
cat_sound = get_animal(CatFactory())
print(f"Dog says: {dog_sound}") # Izlaz: Dog says: Woof!
print(f"Cat says: {cat_sound}") # Izlaz: Cat says: Meow!
Objašnjenje:
Animal: Apstraktna bazna klasa koja definira sučelje za sve vrste životinja.DogiCat: Konkretne klase koje implementiraju sučeljeAnimal.AnimalFactory: Apstraktna bazna klasa koja definira sučelje za stvaranje životinja.DogFactoryiCatFactory: Konkretne klase koje implementiraju sučeljeAnimalFactory, odgovorne za stvaranje instanciDogiCat, respektivno.get_animal: Funkcija klijenta koja koristi tvornicu za stvaranje i korištenje životinje.
Slučajevi upotrebe
- UI Frameworks: Stvaranje elemenata korisničkog sučelja specifičnih za platformu (npr. gumbi, tekstualna polja) pomoću različitih tvornica za različite operativne sustave.
- Razvoj igara: Stvaranje različitih vrsta likova ili objekata u igri na temelju razine igre ili odabira korisnika.
- Obrada dokumenata: Stvaranje različitih vrsta dokumenata (npr. PDF, Word, HTML) pomoću različitih tvornica na temelju željenog izlaznog formata.
Primjer
Razmotrite scenarij u kojem želite stvoriti različite vrste načina plaćanja na temelju odabira korisnika. Evo kako to možete implementirati pomoću Factory Method obrasca:
from abc import ABC, abstractmethod
class Payment(ABC):
@abstractmethod
def process_payment(self, amount):
pass
class CreditCardPayment(Payment):
def process_payment(self, amount):
return f"Processing credit card payment of ${amount}"
class PayPalPayment(Payment):
def process_payment(self, amount):
return f"Processing PayPal payment of ${amount}"
class PaymentFactory(ABC):
@abstractmethod
def create_payment_method(self):
pass
class CreditCardPaymentFactory(PaymentFactory):
def create_payment_method(self):
return CreditCardPayment()
class PayPalPaymentFactory(PaymentFactory):
def create_payment_method(self):
return PayPalPayment()
# Kôd klijenta
def process_payment(factory: PaymentFactory, amount):
payment_method = factory.create_payment_method()
return payment_method.process_payment(amount)
credit_card_payment = process_payment(CreditCardPaymentFactory(), 100)
paypal_payment = process_payment(PayPalPaymentFactory(), 50)
print(credit_card_payment) # Izlaz: Processing credit card payment of $100
print(paypal_payment) # Izlaz: Processing PayPal payment of $50
3. Abstract Factory obrazac
Abstract Factory obrazac pruža sučelje za stvaranje obitelji povezanih ili ovisnih objekata bez specificiranja njihovih konkretnih klasa. Omogućuje vam stvaranje objekata koji su dizajnirani za rad zajedno, osiguravajući dosljednost i kompatibilnost.
Implementacija
Evo Python implementacije Abstract Factory obrasca:
from abc import ABC, abstractmethod
class Button(ABC):
@abstractmethod
def paint(self):
pass
class Checkbox(ABC):
@abstractmethod
def paint(self):
pass
class GUIFactory(ABC):
@abstractmethod
def create_button(self):
pass
@abstractmethod
def create_checkbox(self):
pass
class WinFactory(GUIFactory):
def create_button(self):
return WinButton()
def create_checkbox(self):
return WinCheckbox()
class MacFactory(GUIFactory):
def create_button(self):
return MacButton()
def create_checkbox(self):
return MacCheckbox()
class WinButton(Button):
def paint(self):
return "Rendering a Windows button"
class MacButton(Button):
def paint(self):
return "Rendering a Mac button"
class WinCheckbox(Checkbox):
def paint(self):
return "Rendering a Windows checkbox"
class MacCheckbox(Checkbox):
def paint(self):
return "Rendering a Mac checkbox"
# Kôd klijenta
def paint_ui(factory: GUIFactory):
button = factory.create_button()
checkbox = factory.create_checkbox()
return button.paint(), checkbox.paint()
win_button, win_checkbox = paint_ui(WinFactory())
mac_button, mac_checkbox = paint_ui(MacFactory())
print(win_button) # Izlaz: Rendering a Windows button
print(win_checkbox) # Izlaz: Rendering a Windows checkbox
print(mac_button) # Izlaz: Rendering a Mac button
print(mac_checkbox) # Izlaz: Rendering a Mac checkbox
Objašnjenje:
ButtoniCheckbox: Apstraktne bazne klase koje definiraju sučelja za elemente korisničkog sučelja.WinButton,MacButton,WinCheckboxiMacCheckbox: Konkretne klase koje implementiraju sučelja elemenata korisničkog sučelja za Windows i Mac platforme.GUIFactory: Apstraktna bazna klasa koja definira sučelje za stvaranje obitelji elemenata korisničkog sučelja.WinFactoryiMacFactory: Konkretne klase koje implementiraju sučeljeGUIFactory, odgovorne za stvaranje elemenata korisničkog sučelja za Windows i Mac platforme, respektivno.paint_ui: Funkcija klijenta koja koristi tvornicu za stvaranje i bojanje elemenata korisničkog sučelja.
Slučajevi upotrebe
- UI Frameworks: Stvaranje elemenata korisničkog sučelja koji su u skladu s izgledom i osjećajem određenog operativnog sustava ili platforme.
- Razvoj igara: Stvaranje objekata u igri koji su u skladu sa stilom određene razine igre ili teme.
- Pristup podacima: Stvaranje objekata za pristup podacima koji su kompatibilni s određenom bazom podataka ili izvorom podataka.
Primjer
Razmotrite scenarij u kojem želite stvoriti različite vrste namještaja (npr. stolice, stolovi) s različitim stilovima (npr. moderni, viktorijanski). Evo kako to možete implementirati pomoću Abstract Factory obrasca:
from abc import ABC, abstractmethod
class Chair(ABC):
@abstractmethod
def create(self):
pass
class Table(ABC):
@abstractmethod
def create(self):
pass
class FurnitureFactory(ABC):
@abstractmethod
def create_chair(self):
pass
@abstractmethod
def create_table(self):
pass
class ModernFurnitureFactory(FurnitureFactory):
def create_chair(self):
return ModernChair()
def create_table(self):
return ModernTable()
class VictorianFurnitureFactory(FurnitureFactory):
def create_chair(self):
return VictorianChair()
def create_table(self):
return VictorianTable()
class ModernChair(Chair):
def create(self):
return "Creating a modern chair"
class VictorianChair(Chair):
def create(self):
return "Creating a Victorian chair"
class ModernTable(Table):
def create(self):
return "Creating a modern table"
class VictorianTable(Table):
def create(self):
return "Creating a Victorian table"
# Kôd klijenta
def create_furniture(factory: FurnitureFactory):
chair = factory.create_chair()
table = factory.create_table()
return chair.create(), table.create()
modern_chair, modern_table = create_furniture(ModernFurnitureFactory())
victorian_chair, victorian_table = create_furniture(VictorianFurnitureFactory())
print(modern_chair) # Izlaz: Creating a modern chair
print(modern_table) # Izlaz: Creating a modern table
print(victorian_chair) # Izlaz: Creating a Victorian chair
print(victorian_table) # Izlaz: Creating a Victorian table
4. Builder obrazac
Builder obrazac odvaja konstrukciju složenog objekta od njegove reprezentacije, omogućujući istom procesu konstrukcije da stvori različite reprezentacije. Koristan je kada trebate stvoriti složene objekte s više izbornih komponenti i želite izbjeći stvaranje velikog broja konstruktora ili parametara konfiguracije.
Implementacija
Evo Python implementacije Builder obrasca:
class Pizza:
def __init__(self):
self.dough = None
self.sauce = None
self.topping = None
def __str__(self):
return f"Pizza with dough: {self.dough}, sauce: {self.sauce}, and topping: {self.topping}"
class PizzaBuilder:
def __init__(self):
self.pizza = Pizza()
def set_dough(self, dough):
self.pizza.dough = dough
return self
def set_sauce(self, sauce):
self.pizza.sauce = sauce
return self
def set_topping(self, topping):
self.pizza.topping = topping
return self
def build(self):
return self.pizza
# Kôd klijenta
pizza_builder = PizzaBuilder()
pizza = pizza_builder.set_dough("Thin crust").set_sauce("Tomato").set_topping("Pepperoni").build()
print(pizza) # Izlaz: Pizza with dough: Thin crust, sauce: Tomato, and topping: Pepperoni
Objašnjenje:
Pizza: Klasa koja predstavlja složeni objekt koji se gradi.PizzaBuilder: Klasa graditelja koja pruža metode za postavljanje različitih komponenti objektaPizza.
Slučajevi upotrebe
- Generiranje dokumenata: Stvaranje složenih dokumenata (npr. izvješća, fakture) s različitim odjeljcima i opcijama oblikovanja.
- Razvoj igara: Stvaranje složenih objekata u igri (npr. likovi, razine) s različitim atributima i komponentama.
- Obrada podataka: Stvaranje složenih struktura podataka (npr. grafovi, stabla) s različitim čvorovima i odnosima.
Primjer
Razmotrite scenarij u kojem želite izgraditi različite vrste računala s različitim komponentama (npr. CPU, RAM, pohrana). Evo kako to možete implementirati pomoću Builder obrasca:
class Computer:
def __init__(self):
self.cpu = None
self.ram = None
self.storage = None
self.graphics_card = None
def __str__(self):
return f"Computer with CPU: {self.cpu}, RAM: {self.ram}, Storage: {self.storage}, Graphics Card: {self.graphics_card}"
class ComputerBuilder:
def __init__(self):
self.computer = Computer()
def set_cpu(self, cpu):
self.computer.cpu = cpu
return self
def set_ram(self, ram):
self.computer.ram = ram
return self
def set_storage(self, storage):
self.computer.storage = storage
return self
def set_graphics_card(self, graphics_card):
self.computer.graphics_card = graphics_card
return self
def build(self):
return self.computer
# Kôd klijenta
computer_builder = ComputerBuilder()
computer = computer_builder.set_cpu("Intel i7").set_ram("16GB").set_storage("1TB SSD").set_graphics_card("Nvidia RTX 3080").build()
print(computer)
# Izlaz: Computer with CPU: Intel i7, RAM: 16GB, Storage: 1TB SSD, Graphics Card: Nvidia RTX 3080
5. Prototype obrazac
Prototype obrazac specificira vrstu objekata koje treba stvoriti pomoću prototipne instance i stvara nove objekte kopiranjem ovog prototipa. Omogućuje vam stvaranje novih objekata kloniranjem postojećeg objekta, izbjegavajući potrebu za stvaranjem objekata od nule. To može biti korisno kada je stvaranje objekata skupo ili složeno.
Implementacija
Evo Python implementacije Prototype obrasca:
import copy
class Prototype:
def __init__(self):
self._objects = {}
def register_object(self, name, obj):
self._objects[name] = obj
def unregister_object(self, name):
del self._objects[name]
def clone(self, name, **attrs):
obj = copy.deepcopy(self._objects.get(name))
if attrs:
obj.__dict__.update(attrs)
return obj
class Car:
def __init__(self):
self.name = ""
self.color = ""
self.options = []
def __str__(self):
return f"Car: Name={self.name}, Color={self.color}, Options={self.options}"
# Kôd klijenta
prototype = Prototype()
car = Car()
car.name = "Generic Car"
car.color = "White"
car.options = ["AC", "GPS"]
prototype.register_object("generic", car)
car1 = prototype.clone("generic", name="Sports Car", color="Red", options=["AC", "GPS", "Spoiler"])
car2 = prototype.clone("generic", name="Family Car", color="Blue", options=["AC", "GPS", "Sunroof"])
print(car1)
# Izlaz: Car: Name=Sports Car, Color=Red, Options=['AC', 'GPS', 'Spoiler']
print(car2)
# Izlaz: Car: Name=Family Car, Color=Blue, Options=['AC', 'GPS', 'Sunroof']
Objašnjenje:
Prototype: Klasa koja upravlja prototipovima i pruža metodu za njihovo kloniranje.Car: Klasa koja predstavlja objekt koji se klonira.
Slučajevi upotrebe
- Razvoj igara: Stvaranje objekata u igri koji su slični jedni drugima, kao što su neprijatelji ili pojačanja.
- Obrada dokumenata: Stvaranje dokumenata koji se temelje na predlošku.
- Upravljanje konfiguracijom: Stvaranje objekata konfiguracije koji se temelje na zadanoj konfiguraciji.
Primjer
Razmotrite scenarij u kojem želite stvoriti različite vrste zaposlenika s različitim atributima (npr. ime, uloga, odjel). Evo kako to možete implementirati pomoću Prototype obrasca:
import copy
class Employee:
def __init__(self):
self.name = None
self.role = None
self.department = None
def __str__(self):
return f"Employee: Name={self.name}, Role={self.role}, Department={self.department}"
class Prototype:
def __init__(self):
self._objects = {}
def register_object(self, name, obj):
self._objects[name] = obj
def unregister_object(self, name):
del self._objects[name]
def clone(self, name, **attrs):
obj = copy.deepcopy(self._objects.get(name))
if attrs:
obj.__dict__.update(attrs)
return obj
# Kôd klijenta
prototype = Prototype()
employee = Employee()
employee.name = "Generic Employee"
employee.role = "Developer"
employee.department = "IT"
prototype.register_object("generic", employee)
employee1 = prototype.clone("generic", name="John Doe", role="Senior Developer")
employee2 = prototype.clone("generic", name="Jane Smith", role="Project Manager", department="Management")
print(employee1)
# Izlaz: Employee: Name=John Doe, Role=Senior Developer, Department=IT
print(employee2)
# Izlaz: Employee: Name=Jane Smith, Role=Project Manager, Department=Management
Zaključak
Kreacijski obrasci dizajna pružaju moćne alate za upravljanje stvaranjem objekata na fleksibilan i održiv način. Razumijevanjem i primjenom ovih obrazaca možete pisati čišći, robusniji kôd koji je lakše proširiti i prilagoditi promjenjivim zahtjevima. Ovaj je članak istražio pet ključnih kreacijskih obrazaca—Singleton, Factory Method, Abstract Factory, Builder i Prototype—s praktičnim primjerima i slučajevima upotrebe u stvarnom svijetu. Ovladavanje ovim obrascima bitan je korak u postajanju vještim Python programerom.
Zapamtite da odabir pravog obrasca ovisi o specifičnom problemu koji pokušavate riješiti. Razmotrite složenost stvaranja objekata, potrebu za fleksibilnošću i potencijal za buduće promjene pri odabiru kreacijskog obrasca za svoj projekt. Na taj način možete iskoristiti snagu obrazaca dizajna za stvaranje elegantnih i učinkovitih rješenja za uobičajene izazove dizajna softvera.